package com.atguigu.ggkt.order.service.impl;

import com.atguigu.ggkt.box.OrderBox;
import com.atguigu.ggkt.client.sequence.SequenceFeignClient;
import com.atguigu.ggkt.client.user.UserInfoFeignClient;
import com.atguigu.ggkt.enums.Env;
import com.atguigu.ggkt.enums.OrderStatus;
import com.atguigu.ggkt.enums.PaymentStatus;
import com.atguigu.ggkt.model.order.OrderDetail;
import com.atguigu.ggkt.model.order.OrderInfo;
import com.atguigu.ggkt.model.order.PaymentInfo;
import com.atguigu.ggkt.model.user.UserInfo;
import com.atguigu.ggkt.order.mapper.OrderDetailMapper;
import com.atguigu.ggkt.order.mapper.OrderInfoMapper;
import com.atguigu.ggkt.order.mapper.PaymentInfoMapper;
import com.atguigu.ggkt.order.service.OrderDetailService;
import com.atguigu.ggkt.order.service.OrderInfoService;
import com.atguigu.ggkt.order.service.WxPayService;
import com.atguigu.ggkt.result.Result;
import com.atguigu.ggkt.utils.*;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.model.Transaction;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.time.Instant;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.Map;

/**
 * 微信支付
 * https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/jsapi-transfer-payment.html
 * https://pay.weixin.qq.com/docs/merchant/apis/jsapi-payment/direct-jsons/jsapi-prepay.html
 * https://github.com/wechatpay-apiv3/wechatpay-java/blob/main/service/src/example/java/com/wechat/pay/java/service/QuickStart.java
 *
 * @since 20240815
 */
@Service
@RefreshScope
public class WxPayServiceImpl_v3 implements WxPayService {

    @Value("${wechat.mpAppId}")
    private String mpAppId;
    @Value("${wechat.notifyUrl}")
    private String notifyUrl;
    @Value("${wechat.mch.id-1}")
    private String mchId1;
    @Value("${wechat.mch.id-1-privateKeyPath}")
    private String privateKeyPath;
    @Value("${wechat.mch.id-1-merchantSerialNumber}")
    private String merchantSerialNumber;
    @Value("${wechat.mch.id-1-apiV3Key}")
    private String apiV3Key;
    @Value("${wechat.mch.id-1-sign-pem}")
    private String signPem;

    @Autowired
    private OrderInfoService orderInfoService;
    @Resource
    private SequenceFeignClient sequenceFeignClient;
    @Resource
    private OrderDetailServiceImpl orderDetailServiceImpl;
    @Resource
    OrderInfoMapper orderInfoMapper;
    @Resource
    PaymentInfoMapper paymentInfoMapper;
    @Resource
    OrderDetailMapper orderDetailMapper;
    @Resource
    UserInfoFeignClient userInfoFeignClient;

    public PrepayWithRequestPaymentResponse createJsapiDemo() throws IOException {

        File file = new File(privateKeyPath);
        if (!file.exists()) {
            // 把base64字符串转成文件
            LogTool.info(this.getClass(), "go to create private key file!");
            Base64FileConversion.decodeBase64ToFile(signPem, file);
        }
        // 使用自动更新平台证书的RSA配置
        // 一个商户号只能初始化一个配置，否则会因为重复的下载任务报错
        Config config =
                new RSAAutoCertificateConfig.Builder()
                        .merchantId(mchId1)
                        .privateKeyFromPath(privateKeyPath)
                        .merchantSerialNumber(merchantSerialNumber)
                        .apiV3Key(apiV3Key)
                        .build();
        //JsapiService service = new JsapiService.Builder().config(config).build();
        // request.setXxx(val)设置所需参数，具体参数可见Request定义
        PrepayRequest request = new PrepayRequest();
        Amount amount = new Amount();
        amount.setTotal(1);
        request.setAmount(amount);

        // local环境一样使用 dev 环境的 mpAppId
        request.setAppid(mpAppId);
        String profile = String.valueOf(SpringTool.getProperty("spring.profiles.active"));
        if (profile.equals(Env.local.getCode())){
            LogTool.info(this.getClass(), "use dev mpAppId");
            String mpAppIdDev = NacosTool.getPropertyFromDevCommon("wechat.mpAppId");
            request.setAppid(mpAppIdDev);
        }

        request.setMchid(mchId1);
        request.setDescription("测试商品标题");
        request.setNotifyUrl(notifyUrl);
        Result res = sequenceFeignClient.getOne();
        if (res.getCode() == 20000 && ObjectUtils.isNotEmpty(res.getData())) {
            LogTool.info(this.getClass(), "seq: "+res.getData()+","+ String.valueOf(res.getData()).length());
            request.setOutTradeNo(String.valueOf(res.getData()));
        }

        Payer payer = new Payer();
        payer.setOpenid("oOcfb6Lo30hqv9AUIqm0l91adSWA");
        request.setPayer(payer);

        JsapiServiceExtension service2 = new JsapiServiceExtension.Builder().config(config).build();

        // response包含了调起支付所需的所有参数，可直接用于前端调起支付
        PrepayWithRequestPaymentResponse paymentResponse = service2.prepayWithRequestPayment(request);
        System.out.println("PrepayWithRequestPaymentResponse: "+paymentResponse.toString());

        return paymentResponse;
    }

    @Override
    public PrepayWithRequestPaymentResponse createJsapiv3(String orderNo) throws Exception {

        Config config = SpringTool.getSpringBean(RSAAutoCertificateConfig.class);
        // request.setXxx(val)设置所需参数，具体参数可见Request定义
        PrepayRequest request = new PrepayRequest();
        Amount amount = new Amount();
        amount.setTotal(1);
        request.setAmount(amount);

        // local环境一样使用 dev 环境的 mpAppId
        request.setAppid(mpAppId);
        String profile = String.valueOf(SpringTool.getProperty("spring.profiles.active"));
        if (profile.equals(Env.local.getCode())){
            LogTool.info(this.getClass(), "use dev mpAppId");
            String mpAppIdDev = NacosTool.getPropertyFromDevCommon("wechat.mpAppId");
            request.setAppid(mpAppIdDev);
        }

        request.setMchid(mchId1);
        request.setDescription("测试商品标题");
        request.setNotifyUrl(notifyUrl);
        request.setOutTradeNo(orderNo);

        OrderInfo orderInfo = orderInfoMapper.selectById(orderNo);
        UserInfo userInfo = userInfoFeignClient.getById(orderInfo.getUserId());
        Payer payer = new Payer();
        // payer.setOpenid("oOcfb6Lo30hqv9AUIqm0l91adSWA");
        payer.setOpenid(userInfo.getOpenId());
        request.setPayer(payer);

        JsapiServiceExtension service2 = new JsapiServiceExtension.Builder().config(config).build();

        // response包含了调起支付所需的所有参数，可直接用于前端调起支付
        PrepayWithRequestPaymentResponse paymentResponse = service2.prepayWithRequestPayment(request);
        System.out.println("PrepayWithRequestPaymentResponse: "+paymentResponse.toString());
        if (ObjectUtils.isNotEmpty(paymentResponse) && StringUtils.isNotEmpty(paymentResponse.getPackageVal())){
            OrderBox orderBox = new OrderBox();
            orderBox.setOrderInfo(orderInfo);
            orderBox.setPrepayRequest(request);
            orderBox.setPaymentResponse(paymentResponse);
            orderInfoService.createPaymentInfo(orderBox);
        }
        return paymentResponse;
    }

    public void setOrderBox(String orderNo, OrderBox orderBox){
        OrderInfo orderInfo = orderInfoMapper.selectById(orderNo);
        orderBox.setOrderInfo(orderInfo);
        PaymentInfo paymentInfo = paymentInfoMapper.selectById(orderNo);
        orderBox.setPaymentInfo(paymentInfo);
        OrderDetail orderDetail = orderDetailMapper.selectById(orderNo);
        orderBox.setOrderDetail(orderDetail);
    }

    //根据订单号调用微信接口查询支付状态
    @Override
    public Transaction queryPayStatus(String orderNo, OrderBox box) {
        PaymentInfo paymentInfo = box.getPaymentInfo();
        String mchid = paymentInfo.getMchid();
        String transactionId = paymentInfo.getTransactionId();

        QueryOrderByIdRequest queryRequest = new QueryOrderByIdRequest();
        queryRequest.setMchid(mchid);
        queryRequest.setTransactionId(transactionId);
        Transaction transaction = null;

        try {
            Config config = SpringTool.getSpringBean(RSAAutoCertificateConfig.class);
            JsapiService service = new JsapiService.Builder().config(config).build();
            transaction = service.queryOrderById(queryRequest);
            LogTool.info(this.getClass(), orderNo+",queryPayStatus getTradeState: "+transaction.getTradeState());
        } catch (ServiceException e) {
            // API返回失败, 例如ORDER_NOT_EXISTS
            System.out.printf(orderNo+"code=[%s], message=[%s]\n", e.getErrorCode(), e.getErrorMessage());
            System.out.printf(orderNo+"reponse body=[%s]\n", e.getResponseBody());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return transaction;
    }

    @Override
    public void updateResultAfterWxNotify(Transaction transaction, String requestBody){
        new Thread(() -> {
            LogTool.info(this.getClass(), transaction.getOutTradeNo()+" go to updateResult after wx notify");
            WxPayServiceImpl_v3 bean = SpringTool.getSpringBean(WxPayServiceImpl_v3.class);
            bean.updateResult(transaction, requestBody);
        }).start();
    }

    @Override
    public void updateResult(Transaction transaction, String requestBody){
        OrderInfo orderInfo = orderInfoMapper.selectById(transaction.getOutTradeNo());
        PaymentInfo paymentInfo = paymentInfoMapper.selectById(transaction.getOutTradeNo());
        OrderDetail orderDetail = orderDetailMapper.selectById(transaction.getOutTradeNo());

        orderInfo.setPayTime(DateUtil.parseIsoDateTime(transaction.getSuccessTime()));
        orderInfo.setUpdateTime(DateUtil.now());

        orderInfo.setOrderStatus(OrderStatus.PAID.getCode().toString());
        orderInfoMapper.updateById(orderInfo);

        orderDetail.setOrderStatus(OrderStatus.PAID.getCode().toString());
        orderDetailMapper.updateById(orderDetail);

        paymentInfo.setTransactionId(transaction.getTransactionId());
        paymentInfo.setPaymentStatus(PaymentStatus.PAID.getCode().toString());
        paymentInfoMapper.updateById(paymentInfo);
        LogTool.info(this.getClass(), transaction.getOutTradeNo()+" ok <<< updateResult after wx notify");

    }


}
